package mws;

public class SimpleReadWriteLock {
    private int readCount = 0;
    private int writeCount = 0;
    private Thread writingThread = null;
    private final Object lock = new Object();
    public SimpleReadWriteLock.ReadLock getReadLock(){
        return new SimpleReadWriteLock.ReadLock();
    }
    public SimpleReadWriteLock.WriteLock getWriteLock(){
        return new SimpleReadWriteLock.WriteLock();
    }

    class ReadLock {
        public void lock() {
            synchronized (lock) {
                while (writeCount > 0) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
                readCount++;
            }
        }

        public void unlock() {
            synchronized (lock) {
                readCount--;
                if (readCount == 0) {
                    lock.notifyAll();
                }
            }
        }
    }

    class WriteLock {
        public void lock() {
            synchronized (lock) {
                // 如果是重入
                if (writingThread == Thread.currentThread()) {
                    writeCount++;
                    return;
                }

                while (readCount > 0 || writeCount > 0) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }

                writeCount++;
                writingThread = Thread.currentThread();
            }
        }

        public void unlock() {
            synchronized (lock) {
                if (writingThread != Thread.currentThread()) {
                    throw new IllegalMonitorStateException();
                }

                writeCount--;
                if (writeCount == 0) {
                    writingThread = null;
                    lock.notifyAll();
                }
            }
        }
    }
}